#include "Cube.h"
#include <iostream>

Cube::Cube(float size)
{
    using namespace Nitro::Renderer;
    auto s = size / 2.0f;

    ShaderDescriptor shaderDesc;
    shaderDesc.type = ShaderType::VertexShader;
    shaderDesc.sourceType = ShaderSourceType::SourceCode;
    shaderDesc.source = R"delimiter(
    #version 450 core
    in vec3 pos;
    in vec2 texureCoord;
    uniform mat4 modelMatrix;
    uniform mat4 viewProjectionMatrix;
    out vec2 texCoord;
    void main() {
        gl_Position = viewProjectionMatrix * modelMatrix * vec4(pos, 1.0f);
        texCoord = texureCoord;
    }
    )delimiter";
    vs = CreateShader(shaderDesc);
    if (vs.hasReport())
    {
        std::cout << vs.getReport();
    }
    shaderDesc.type = ShaderType::FragmentShader;
    shaderDesc.sourceType = ShaderSourceType::SourceCode;
    shaderDesc.source = R"delimiter(
    #version 450 core
    in vec2 texCoord;
    out vec4 fragColor;
    uniform sampler2D cubeTexture;
    void main() {
        fragColor = texture(cubeTexture, texCoord);
    }
    )delimiter";
    fs = CreateShader(shaderDesc);
    if (fs.hasReport())
    {
        std::cout << fs.getReport();
    }

    PipelineDescriptor pipelineDesc;
    pipelineDesc.vertexShader = vs;
    pipelineDesc.fragmentShader = fs;
    pipelineDesc.drawMode = DrawMode::Triangles;
    pipeline = CreatePipeline(pipelineDesc);
    if (pipeline.hasReport())
    {
        std::cout << pipeline.getReport();
    }

    struct Vertex
    {
        float position[3], texCoord[2];
    } verts[] = {
        s,  s,  s,  s,    s,    //
        s,  -s, s,  s,    0.0f, //
        -s, -s, s,  0.0f, 0.0f, //
        -s, s,  s,  0.0f, s,    //

        s,  s,  -s, s,    s,    //
        s,  -s, -s, s,    0.0f, //
        -s, -s, -s, 0.0f, 0.0f, //
        -s, s,  -s, 0.0f, s,    //

        s,  s,  s,  s,    s,    //
        s,  -s, s,  0.0f, s,    //
        s,  s,  -s, s,    0.0f, //
        s,  -s, -s, 0.0f, 0.0f, //

        -s, -s, s,  0.0f, 0.0f, //
        -s, s,  s,  s,    0.0f, //
        -s, -s, -s, 0.0f, s,    //
        -s, s,  -s, s,    s,    //

        s,  s,  s,  0.0f, 0.0f, //
        -s, s,  s,  s,    0.0f, //
        s,  s,  -s, 0.0f, s,    //
        -s, s,  -s, s,    s,    //

        s,  -s, s,  0.0f, 0.0f, //
        -s, -s, s,  s,    0.0f, //
        s,  -s, -s, 0.0f, s,    //
        -s, -s, -s, s,    s,    //
    };

    std::vector<uint32_t> indices = {
        0,  1,  2,  0,  2,  3,  //
        4,  5,  6,  4,  6,  7,  //
        8,  9,  11, 8,  10, 11, //
        12, 13, 15, 12, 14, 15, //
        16, 17, 19, 16, 18, 19, //
        20, 21, 23, 20, 22, 23, //
    };

    BufferDescriptor bufferDesc;
    bufferDesc.bindFlags = BindFlags::VertexBuffer;
    bufferDesc.size = sizeof(verts);
    bufferDesc.appendAttribute(0, sizeof(Vertex), Format::RGB32Float);
    bufferDesc.appendAttribute(3 * sizeof(float), sizeof(Vertex),
                               Format::RG32Float);
    vertexBuffer = CreateBuffer(bufferDesc, verts);

    bufferDesc.bindFlags = BindFlags::IndexBuffer;
    bufferDesc.size = sizeof(decltype(indices)::value_type) * indices.size();
    indexBuffer = CreateBuffer(bufferDesc, indices.data());

    int w, h, n;
    SourceImageDescriptor srcDesc;
    TextureDescriptor2D texDesc;
    auto data = stbi_load("cube.jpg", &w, &h, &n, 3);
    srcDesc.data = data;
    srcDesc.format = Format::RGB8UInt;
    srcDesc.width = w;
    srcDesc.height = h;
    texDesc.minFilter = TextureFilter::LinearMipmapLinear;
    texture = CreateTexture2D(texDesc, srcDesc);
    texture.generateMipmap();
    stbi_image_free(data);
}

void Cube::render()
{
    updateMatrix();
    pipeline.activate();
    pipeline.setUniform("modelMatrix", modelMatrix);
    pipeline.setUniform("viewProjectionMatrix", viewProjectionMatrix);
    glBindTexture(GL_TEXTURE_2D, texture.id);
    pipeline.setVertexBuffer(vertexBuffer);
    pipeline.setIndexBuffer(indexBuffer);
    pipeline.drawIndexed(0, 36);
}